]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Use StartupWMClass to associate window and applications
authorGiovanni Campagna <gcampagna@src.gnome.org>
Fri, 6 Apr 2012 18:02:04 +0000 (20:02 +0200)
committerGiovanni Campagna <gcampagna@src.gnome.org>
Wed, 7 Aug 2013 10:17:42 +0000 (12:17 +0200)
Some applications (such as most Java apps, as well as Chrome Web apps) ship
with desktop files that have the wrong name, but whose StartupWMClass
field contains the right value. Therefore first check that key, against
both the class and instance part of WM_CLASS, and only use the filename
if nothing else works.

https://bugzilla.gnome.org/show_bug.cgi?id=673657

src/shell-app-system.c
src/shell-app-system.h
src/shell-window-tracker.c

index 6a96e0ca876207b69866c362dd2c952285051ebb..a6587bef644196b1b142923031db0bd76360d1e6 100644 (file)
@@ -43,6 +43,7 @@ struct _ShellAppSystemPrivate {
   GHashTable *running_apps;
   GHashTable *visible_id_to_app;
   GHashTable *id_to_app;
+  GHashTable *startup_wm_class_to_app;
 
   GSList *known_vendor_prefixes;
 };
@@ -93,6 +94,10 @@ shell_app_system_init (ShellAppSystem *self)
   /* All the objects in this hash table are owned by id_to_app */
   priv->visible_id_to_app = g_hash_table_new (g_str_hash, g_str_equal);
 
+  priv->startup_wm_class_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                         NULL,
+                                                         (GDestroyNotify)g_object_unref);
+
   /* We want to track NoDisplay apps, so we add INCLUDE_NODISPLAY. We'll
    * filter NoDisplay apps out when showing them to the user. */
   priv->apps_tree = gmenu_tree_new ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
@@ -112,6 +117,7 @@ shell_app_system_finalize (GObject *object)
   g_hash_table_destroy (priv->running_apps);
   g_hash_table_destroy (priv->id_to_app);
   g_hash_table_destroy (priv->visible_id_to_app);
+  g_hash_table_destroy (priv->startup_wm_class_to_app);
 
   g_slist_free_full (priv->known_vendor_prefixes, g_free);
   priv->known_vendor_prefixes = NULL;
@@ -323,9 +329,11 @@ on_apps_tree_changed_cb (GMenuTree *tree,
       GMenuTreeEntry *old_entry;
       char *prefix;
       ShellApp *app;
-      
+      GDesktopAppInfo *info;
+      const char *startup_wm_class;
+
       prefix = get_prefix_for_entry (entry);
-      
+
       if (prefix != NULL
           && !g_slist_find_custom (self->priv->known_vendor_prefixes, prefix,
                                    (GCompareFunc)g_strcmp0))
@@ -333,7 +341,7 @@ on_apps_tree_changed_cb (GMenuTree *tree,
                                                             prefix);
       else
         g_free (prefix);
-      
+
       app = g_hash_table_lookup (self->priv->id_to_app, id);
       if (app != NULL)
         {
@@ -361,6 +369,24 @@ on_apps_tree_changed_cb (GMenuTree *tree,
       if (!gmenu_tree_entry_get_is_nodisplay_recurse (entry))
         g_hash_table_replace (self->priv->visible_id_to_app, (char*)id, app);
 
+      if (old_entry)
+        {
+          GDesktopAppInfo *old_info;
+          const gchar *old_startup_wm_class;
+
+          old_info = gmenu_tree_entry_get_app_info (old_entry);
+          old_startup_wm_class = g_desktop_app_info_get_startup_wm_class (old_info);
+
+          if (old_startup_wm_class)
+            g_hash_table_remove (self->priv->startup_wm_class_to_app, old_startup_wm_class);
+        }
+
+      info = gmenu_tree_entry_get_app_info (entry);
+      startup_wm_class = g_desktop_app_info_get_startup_wm_class (info);
+      if (startup_wm_class)
+        g_hash_table_replace (self->priv->startup_wm_class_to_app,
+                              (char*)startup_wm_class, g_object_ref (app));
+
       if (old_entry)
         gmenu_tree_item_unref (old_entry);
     }
@@ -513,17 +539,18 @@ shell_app_system_lookup_heuristic_basename (ShellAppSystem *system,
 }
 
 /**
- * shell_app_system_lookup_wmclass:
+ * shell_app_system_lookup_desktop_wmclass:
  * @system: a #ShellAppSystem
  * @wmclass: A WM_CLASS value
  *
- * Find a valid application corresponding to a WM_CLASS value.
+ * Find a valid application whose .desktop file, without the extension
+ * and properly canonicalized, matches @wmclass.
  *
  * Returns: (transfer none): A #ShellApp for @wmclass
  */
 ShellApp *
-shell_app_system_lookup_wmclass (ShellAppSystem *system,
-                                 const char     *wmclass)
+shell_app_system_lookup_desktop_wmclass (ShellAppSystem *system,
+                                         const char     *wmclass)
 {
   char *canonicalized;
   char *desktop_file;
@@ -548,6 +575,23 @@ shell_app_system_lookup_wmclass (ShellAppSystem *system,
   return app;
 }
 
+/**
+ * shell_app_system_lookup_startup_wmclass:
+ * @system: a #ShellAppSystem
+ * @wmclass: A WM_CLASS value
+ *
+ * Find a valid application whose .desktop file contains a
+ * StartupWMClass entry matching @wmclass.
+ *
+ * Returns: (transfer none): A #ShellApp for @wmclass
+ */
+ShellApp *
+shell_app_system_lookup_startup_wmclass (ShellAppSystem *system,
+                                         const char     *wmclass)
+{
+  return g_hash_table_lookup (system->priv->startup_wm_class_to_app, wmclass);
+}
+
 void
 _shell_app_system_notify_app_state_changed (ShellAppSystem *self,
                                             ShellApp       *app)
index 243a6c028fefb8a62c7751e897bcc5bf6ec6ef41..6b3cd89eeecc845422927fbdfdf8df5642be92fb 100644 (file)
@@ -49,7 +49,10 @@ ShellApp       *shell_app_system_lookup_app_for_path          (ShellAppSystem  *
                                                                const char      *desktop_path);
 ShellApp       *shell_app_system_lookup_heuristic_basename    (ShellAppSystem  *system,
                                                                const char      *id);
-ShellApp       *shell_app_system_lookup_wmclass               (ShellAppSystem *system,
+
+ShellApp       *shell_app_system_lookup_startup_wmclass       (ShellAppSystem *system,
+                                                               const char     *wmclass);
+ShellApp       *shell_app_system_lookup_desktop_wmclass       (ShellAppSystem *system,
                                                                const char     *wmclass);
 
 GSList         *shell_app_system_get_running               (ShellAppSystem  *self);
index d2cc7a82c375d52be1c6ffebf828ddad05117da6..b526ff322b80a79f089cbcdf760cf44488af4b07 100644 (file)
@@ -177,6 +177,82 @@ shell_window_tracker_is_window_interesting (MetaWindow *window)
   return TRUE;
 }
 
+/**
+ * get_app_from_window_wmclass:
+ *
+ * Looks only at the given window, and attempts to determine
+ * an application based on WM_CLASS.  If one can't be determined,
+ * return %NULL.
+ *
+ * Return value: (transfer full): A newly-referenced #ShellApp, or %NULL
+ */
+static ShellApp *
+get_app_from_window_wmclass (MetaWindow  *window)
+{
+  ShellApp *app;
+  ShellAppSystem *appsys;
+  char *appid;
+  const char *wm_class;
+  const char *wm_instance;
+  char *with_desktop;
+
+  appsys = shell_app_system_get_default ();
+
+  /* Notes on the heuristics used here:
+     much of the complexity here comes from the desire to support
+     Chrome apps.
+
+     From https://bugzilla.gnome.org/show_bug.cgi?id=673657#c13
+
+     Currently chrome sets WM_CLASS as follows (the first string is the 'instance',
+     the second one is the 'class':
+
+     For the normal browser:
+     WM_CLASS(STRING) = "chromium", "Chromium"
+
+     For a bookmarked page (through 'Tools -> Create application shortcuts')
+     WM_CLASS(STRING) = "wiki.gnome.org__GnomeShell_ApplicationBased", "Chromium"
+
+     For an application from the chrome store (with a .desktop file created through
+     right click, "Create shortcuts" from Chrome's apps overview)
+     WM_CLASS(STRING) = "crx_blpcfgokakmgnkcojhhkbfbldkacnbeo", "Chromium"
+
+     The .desktop file has a matching StartupWMClass, but the name differs, e.g. for
+     the store app (youtube) there is
+
+     .local/share/applications/chrome-blpcfgokakmgnkcojhhkbfbldkacnbeo-Default.desktop
+
+     with
+
+     StartupWMClass=crx_blpcfgokakmgnkcojhhkbfbldkacnbeo
+  */
+
+  /* first try a match from WM_CLASS to StartupWMClass */
+  wm_class = meta_window_get_wm_class (window);
+  app = shell_app_system_lookup_startup_wmclass (appsys, wm_class);
+  if (app != NULL)
+    return g_object_ref (app);
+
+  /* then try a match from WM_CLASS (instance part) to StartupWMClass */
+  wm_instance = meta_window_get_wm_class_instance (window);
+  app = shell_app_system_lookup_startup_wmclass (appsys, wm_instance);
+  if (app != NULL)
+    return g_object_ref (app);
+
+  /* then try a match from WM_CLASS to .desktop */
+  app = shell_app_system_lookup_desktop_wmclass (appsys, wm_class);
+  if (app != NULL)
+    return g_object_ref (app);
+
+  /* finally, try a match from WM_CLASS (instance part) to .desktop
+     (unlikely to find anything at this point, but still worth a try) */
+  app = shell_app_system_lookup_desktop_wmclass (appsys, wm_instance);
+  if (app != NULL)
+    return g_object_ref (app);
+
+  return NULL;
+}
+
 /**
  * get_app_from_window_group:
  * @monitor: a #ShellWindowTracker
@@ -296,8 +372,7 @@ get_app_for_window (ShellWindowTracker    *tracker,
   /* Check if the app's WM_CLASS specifies an app; this is
    * canonical if it does.
    */
-  result = shell_app_system_lookup_wmclass (app_system,
-                                            meta_window_get_wm_class (window));
+  result = get_app_from_window_wmclass (window);
   if (result != NULL)
     return g_object_ref (result);